#include <GL/freeglut.h>
#include "Polygon3D.h"
#include <assert.h>
#include <iostream>
#include <algorithm>
#include <boost/bind.hpp>

using namespace Eigen;
using namespace libPortal;
using namespace std;

Polygon3D p(Hyperplane<double, 3>(Vector3d(0,1,0), 0));
vector<Vector4d> verts;
Vector2i mousePos;
int mouseButton;
Vector2i offset(500,300);
bool moving = false;
vertex_iter pressed = p.end();

//cut variables
bool makingCut = false;
Vector2d startCut, stopCut;

double distVecs(const Vector2d &a, const Vector2i &b) {
  Vector2d casted(b.cast<double>());
  return (a-casted).norm();
}

bool closer(const Vector2d &a, const Vector2d &b, const Vector2i &c) {
  Vector2d casted(c.cast<double>());
  double da = (a-casted).squaredNorm();
  double db = (b-casted).squaredNorm();
  if(da<db)
    return true;
  return false;
}

void mousePressed(int button, int state, int x, int y) {
  if(state==GLUT_UP && makingCut) {
    makingCut = false;
    Vector3d a, b, c;
    startCut -= offset.cast<double>();
    stopCut -= offset.cast<double>();
    a[0] = startCut[0];
    a[1] = 0.0;
    a[2] = startCut[1];
    b[0] = stopCut[0];
    b[1] = 0.0;
    b[2] = stopCut[1];
    c = b;
    c[1] = 1.0;
    Hyperplane<double, 3> cutPlane = Hyperplane<double, 3>::Through(a,b,c);
    p.cut(cutPlane);
  }

  if(state==GLUT_DOWN) {
    mouseButton = button;
    if(mouseButton == GLUT_RIGHT_BUTTON) {
      makingCut = true;
      startCut = Vector2d(x,y);
    } else
      makingCut = false;
  }
  mousePos = Vector2i(x,y);

  //See if we're moving a vertex.
  vertex_iter minIter = min_element(p.begin(), p.end(), boost::bind(closer, _1, _2, mousePos-offset));
  double dist = distVecs(*minIter, mousePos-offset);
  if(dist<=3) {
    moving = true;
    pressed = minIter;
  } else  {
    moving = false;
    pressed = p.end();
  }
}

//called only when a button is depressed.
void mouseMove(int x, int y) {
  Vector2i delta(Vector2i(x,y)-mousePos);
  if(moving) {
    *pressed += delta.cast<double>();
  } else if(makingCut) {
    stopCut = Vector2d(x,y);
  } else {
    offset += delta;
  }
  mousePos = Vector2i(x,y);
}

void reshape(int w, int h)
{
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0, w, h, 0);
  glMatrixMode(GL_MODELVIEW);
}

void display() {
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  glPointSize(3);
  verts.resize(p.numVerts());
  p.triangleDecomposition(verts, true);

  vector<Vector4d>::const_iterator vIter, vEnd;
  vEnd = verts.end();
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  glColor3f(0.0, 0.5, 0.0);
  glPushMatrix();
  glTranslated(offset[0], offset[1], 0);
  glBegin(GL_TRIANGLE_FAN);
  for(vIter=verts.begin();vIter!=vEnd;++vIter) {
    Vector4d vec(*vIter);
    glVertex2d(vec[0], vec[2]);
  }
  glEnd();

  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  glColor3f(0.0, 1.0, 0.0);
  glPushMatrix();
  glBegin(GL_TRIANGLE_FAN);
  for(vIter=verts.begin();vIter!=vEnd;++vIter) {
    Vector4d vec(*vIter);
    glVertex2d(vec[0], vec[2]);
  }
  glEnd();
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);  

  glColor3f(1.0, 0.0, 0.0);
  glBegin(GL_POINTS);
  for(vIter=verts.begin();vIter!=vEnd;++vIter) {
    Vector4d vec(*vIter);
    glVertex2d(vec[0], vec[2]);
  }
  glEnd();
  glPopMatrix();
  glutSwapBuffers();
}

void tick() {
  glutPostRedisplay();
}

int main(int argc, char **argv) {
  p.append(Vector3d(-100,0,-100));
  p.append(Vector3d(-100,0,100));
  p.append(Vector3d(0,0,150));
  p.append(Vector3d(100,0,100));
  p.append(Vector3d(100,0,-100));
  p.append(Vector3d(0,0,-150));
  //p.cut(Hyperplane<double, 3>(Vector3d(0,0,1), 0));

  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowSize(1024, 768);
  glutCreateWindow("polygon test");
  glClearColor(0.0, 0.0, 0.0, 1.0);
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutMotionFunc(mouseMove);
  glutMouseFunc(mousePressed);
  glutIdleFunc(tick);
  glutMainLoop();
  return 0; 
}
